//Copyright 2010 Neurosciences Research Foundation, Incorporated
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <string>
#include <vector>
#include "constants.h"
#include "neuronspecs.h"
#include "microcircuitry.h"
#include "areas.h"
#include "groups.h"

#ifndef NEURONS 
#define NEURONS

using namespace std;

class neurons{
	
public:
	
	// Pre: areas.dat exists and microcircuitry.dat exists (written by matlab function make_microcircuitry.m)
	// Post: connect.dat and groups.dat written and group_data filled and neuron_positions[neuron][*] filled with 3d neuron positions.
	void set_positions(	group group_data[MAX_AREAS][MAX_TYPES], float neuron_positions[][3], bool write_files){

		
		int next_neuron_id = 0;
		
		float x,y, width_mm;
		string area_name;
		int N; 
		areas area_data;
		
		microcircuitry<MAX_AREAS,MAX_TYPES,Layers> micro;

		area_data.load_areas("areas.dat");
		
		neuronspecs ns;
		ns.read_neuronspecs();
		Names = ns.get_neuron_names();
		Ntypes = ns.get_num_types();
		
		micro.read_microcircuitry();
		
		FILE* fs, *fs_group_list;
		if( write_files ){
			fs= fopen("connect.dat","w");
			fs_group_list = fopen("groups.dat","w");
		}
		
		for(int i = 0; i < area_data.get_N_areas(); i++){
			area_name = area_data.get_area_name(i);
			N = area_data.get_N(area_name);
			micro.get_neuron_percentages(Types,area_name);
			
			// Determine the grid size for each neuron type depending on the population size.
			// This will come in handy when allocating neurons and connections.
			vector<int> itype(Ntypes);
			for (int j=0;j<Ntypes;j++) {
				float temp = N*Types[j]/100;
				int side =int( sqrt((double) temp));
				if( side < 1 && temp > 0.001 ){
					side = 1;
				}
				itype[j]=side;
				printf("(%d , %f) ",itype[j]*itype[j],temp);
			};
						
			next_neuron_id =  allocate_area( area_name, area_data.get_area(area_name), itype , next_neuron_id, fs, fs_group_list, group_data, neuron_positions, write_files);	
		}
		
		if( write_files ){
			fclose(fs);
			fclose(fs_group_list);
		}
	}
	
	
	
private:
	
	// Pre: Names structure has been initialized with the names of each Neuron type.
	int allocate_area(string area_name, areas::area area_data, vector<int> itype, int first_neuron_id, FILE* fs, FILE* fs_group_list, group group_data[MAX_AREAS][MAX_TYPES], float neuron_positions[][3], bool write_files)
	{
		int		i,j,k;
		float	x,y,z,kx,ky,xint,yint;
		
		float x0 = area_data.x0;
		float y0 = area_data.y0;
		float width_mm = area_data.width_mm;
		
		int neuron = first_neuron_id;
		for (i=0;i<Ntypes;i++) {
			float step = 1.0f/(itype[i]+1);
			y=step;
			int first_neuron_in_group = neuron;
			for (j=0;j<itype[i];j++) {
				x=step;
				for( k = 0; k < itype[i];k++){
					
					if( write_files ){
						fprintf(fs,"%f\t%f\t%f\t%d\t%s\n",x0+x*width_mm,y0+y*width_mm,0.0, (int)i,area_name.c_str());
					}
					neuron_positions[neuron][0]=x0+x*width_mm;
					neuron_positions[neuron][1]=y0+y*width_mm;
					neuron_positions[neuron][2]=0.0f;
					
					x+=step;
					
					neuron++;
				}
				y+=step;
			};
			
			// Print out group info for analysis and I/O control.
			
			group g( area_name.c_str(), Names[i].c_str(), itype[i], itype[i], first_neuron_in_group, neuron-1, area_data.x0, area_data.y0, area_data.width_mm   );
					
			if(write_files)	g.write(fs_group_list);
			
			group_data[area_data.area_number][i]=g;
			
		};
		
		return neuron;
		
		
	}
	
	float          Types[MAX_TYPES];
	vector<string> Names;

	int Ntypes;

	
};	
#endif
